﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;

namespace MailboxRightsLookup
{
    // Inicialmente los había implementado según el código de Michael Weinhardt y Rockford Lhotka de MSDN
    // http://msdn.microsoft.com/en-us/library/ms993236.aspx
    // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet/html/vbnet01272004.asp
    // La solución es muy elegante pero al usar reflexión no es del todo rápida
    // Al final he optado por otra implementación que comentan en:
    // http://stackoverflow.com/questions/249779/datagridview-sort-and-e-g-bindinglistt-in-net

    public class SortableBindingList<T> : System.ComponentModel.BindingList<T>
    {
        bool isSorted;
        PropertyDescriptor sortProperty;
        ListSortDirection sortDirection;

        // Reemplazo los métodos virtuales relacionados con la búsqueda
        protected override bool SupportsSortingCore
        {
            get { return true; }
        }

        protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
        {
            // Obtengo la lista que internamente encapsula BindingList<T>
            List<T> items = this.Items as List<T>;

            // Aplico y establezco el orden, si hay items que ordenar
            if (items != null)
            {
                // Hay que tener en cuenta que tengo varios tipos nullables como int?,
                // que no son comparables, pero si lo es el tipo subyacente.
                // Teniendo en cuenta que Nullable<T>, al hacer un cast a IComparable
                // hace un unbox del tipo T, en realidad sí que los puedo comparar
                // http://stackoverflow.com/questions/1262996/int32-with-icomparable
                Type type = property.PropertyType;
                Type underlyingType;
                // Este if es un poco críptico, pero es la única forma para evitar evaluar
                // la segunda parte del || dentro del if en casos de que la propiedad no sea Nullable
                // El rendimiento es crítico es esta función
                if (type.GetInterface("IComparable") != null || (type.IsValueType ? 
                        ((underlyingType = Nullable.GetUnderlyingType(type)) != null ?
                        underlyingType.GetInterface("IComparable") != null : false) : false))
                {
                    items.Sort(new Comparison<T>(delegate(T x, T y)
                    {
                        // Comparo x a y si x es no nulo. Si x es nulo pero y no,
                        // comparo y a x e invierto el resultado. Si ambos son nulos, son iguales
                        object xValue, yValue;
                        if ((xValue = property.GetValue(x)) != null)
                            return ((IComparable)xValue).CompareTo(property.GetValue(y)) *
                                (direction == ListSortDirection.Descending ? -1 : 1);
                        else if ((yValue = property.GetValue(y)) != null)
                            return ((IComparable)yValue).CompareTo(property.GetValue(x)) *
                                (direction == ListSortDirection.Descending ? -1 : 1);
                        else
                            return 0;
                    }));
                }
            }
            isSorted = true;
            sortProperty = property;
            sortDirection = direction;

            // Aviso a los controles enlazados (bound) que tienen que actualizar la vista
            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        protected override bool IsSortedCore
        {
            get { return isSorted; }
        }

        protected override void RemoveSortCore()
        {
            isSorted = false;
        }

        protected override PropertyDescriptor SortPropertyCore
        {
            get { return sortProperty; }
        }

        protected override ListSortDirection SortDirectionCore
        {
            get { return sortDirection; }
        }

    }
}